package com.atguigu.gmall.cart.interceptor;

import com.atguigu.gmall.cart.bean.UserInfo;
import com.atguigu.gmall.cart.config.JwtProperties;
import com.atguigu.gmall.common.exception.GmallUserException;
import com.atguigu.gmall.common.utils.CookieUtils;
import com.atguigu.gmall.common.utils.IpUtils;
import com.atguigu.gmall.common.utils.JwtUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.UUID;

@Component
public class LoginInterceptor implements HandlerInterceptor {
    private static ThreadLocal<UserInfo> THREAD_LOCAL = new ThreadLocal<>();

    @Resource
    JwtProperties jwtProperties;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //userKey:无论用户是否登录，如果没有userKey都设置一个  (cookie: 浏览器每次访问时会自动携带)
        String userKey = CookieUtils.getCookieValue(request,
                jwtProperties.getUserKey());
        if(StringUtils.isEmpty(userKey)){
            userKey = IdWorker.getIdStr();
            //第一次为客户端生成userKey时需要设置到响应报文中
            CookieUtils.setCookie(request,response,jwtProperties.getUserKey(),
                    userKey , jwtProperties.getExpire().intValue()*60 );
        }
        UserInfo userInfo = new UserInfo();
        userInfo.setUserKey(userKey);
        //userId:如果用户已登录获取使用
        //异步请求：获取请求头token的值
        String token = request.getHeader(jwtProperties.getToken());
        if(StringUtils.isEmpty(token)){
            token = CookieUtils.getCookieValue(request, jwtProperties.getCookieName());
        }
        if(StringUtils.isNotEmpty(token)){
            Map<String, Object> map = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
            String ip = map.get("ip").toString();
            String ipAddressAtService = IpUtils.getIpAddressAtService(request);
            if(!StringUtils.equals(ip,ipAddressAtService)){
                throw new GmallUserException("ip地址错误",1003);
            }
            long userId = Long.parseLong(map.get("userId").toString());
            userInfo.setUserId(userId);
        }
        THREAD_LOCAL.set(userInfo);
        return true;
    }

    public static UserInfo userInfo(){
        return THREAD_LOCAL.get();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除当前线程的局部变量数据 避免内存泄露
        THREAD_LOCAL.remove();
    }
}
//    @Override
//    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        //false表示拦截请求不放行，true表示放行
//        System.out.println("hehe...........");
//        /*
//         如果用户未登录：后端生成一个唯一的key设置到cookie中响应给浏览器
//                                    userKey --> uuid
//                    如果用户已登录： 使用用户的会员id(userId)当做用户的身份
//         */
//        UserInfo userInfo = new UserInfo();
//        userInfo.setUserId(1L);
//        userInfo.setUserKey(UUID.randomUUID().toString().replace("-",""));
////        request.setAttribute("userInfo",userInfo);
////        ThreadLocal
//        //本地线程变量： 可以在一个线程的生命周期中共享数据  线程安全
//        /*
//            ThreadLocal共享数据的原理：
//                1、线程类有一个成员属性：ThreadLocal.ThreadLocalMap threadLocals
//                2、当调用threadLocal对象的set方法时：
//                    如果当前线程对象的成员属性threadLocals不为空： 使用threadLocal对象作为键，使用传入的值作为值存入
//                    如果线程对象的成员属性threadLocals为空： 新创建ThreadLocalMap对象并设置给当前线程对象的threadLocals成员属性
//                3、在调用 threadLocal对象的get方法时：
//                    使用threadLocal对象作为键从当前线程对象的成员属性threadLocals中获取
//                4、只要当前线程执行期间访问到的方法 都可以通过同一个ThreadLocal对象绑定数据传递
//         */
//        THREAD_LOCAL.set(userInfo);
//        return true;
//    }
